#ifndef FSTRAVERSEITERATOR_H
#define FSTRAVERSEITERATOR_H

#include <iterator>

#include <QDir>
#include <QFileInfo>
#include <QFileInfoList>
#include <QString>

#include "depth1stiterator.h"


class QFileInfoLess : public std::binary_function<QFileInfo, QFileInfo, bool>
{
public:
    bool operator()(const QFileInfo & first,
                    const QFileInfo & second) const;
};


/**
  * Implements the GRAPH role / functionality required by the
  * depth-first iterator: it tells about adjacents of nodes. In
  * this particular case, it can tell what files are in directories.
  */
class FSGraph
{
    bool     m_filesAllowed;
public:
    FSGraph(bool filesAllowed);

    template<typename OUT_ITER>
    void getAdjacents(const QFileInfo & file,
                      OUT_ITER          outIter);
};


/**
  * This iterator can iterate files and directories, recursively,
  * from a starting point (absolute path). One can filter files
  * and directories, as well as control when directories should be
  * iterated: before its content files (DirsPreOrder flag) or
  * after its content files (DirsPortOrder flag) or at both times
  * (DirsBoth).
  *
  * The default is to iterate through both files and directories,
  * with parent directories preceding their contents.
  */
class FSTraverseIterator : public std::iterator<std::forward_iterator_tag, QFileInfo>
{
public:
    //
    // public types
    //
    enum Flag
    {
        DirsPreOrder  = 1,
        DirsPostOrder = 2,
        DirsBoth      = DirsPreOrder | DirsPostOrder,
        Files         = 8,
        FilesAndDirs  = DirsPreOrder | Files,
        Default       = FilesAndDirs
    };


private:
    //
    // private members
    //
    typedef Depth1stIterator<FSGraph, QFileInfo, QFileInfoLess> Iterator;
    FSGraph           m_graph;
    int               m_flags;
    int               m_iteratorFlags;
    int               m_allowedFileColor;
    int               m_allowedDirColors;
    Iterator          m_iterator;
    QFileInfo         m_currentFileInfo;

public:
    //
    // public operators
    //
    /**
      * Constructs a "begin" iterator that will start
      * traversing from given absolute path.
      */
    FSTraverseIterator(const QString  & startingAbsolutePath,
                       int              flags = Default);
    /**
      * Constructs a "begin" iterator that will start
      * traversing from a set of given absolute paths.
      */
    FSTraverseIterator(const QStringList  & startingAbsolutePaths,
                       int                  flags = Default);
    /**
      * Constructs an "end" iterator.
      */
    FSTraverseIterator();

    QFileInfo * operator->();
    QFileInfo & operator*();
    FSTraverseIterator & operator++();
    FSTraverseIterator operator++(int);

private:
    //
    // implementation details
    //
    void next(bool initializationTime = false);
    void setupFlagsAndColors();
    bool allowed(const ColoredNode<QFileInfo> & coloredNode);

    friend bool operator==(const FSTraverseIterator &,
                           const FSTraverseIterator &);
    friend bool operator!=(const FSTraverseIterator &,
                           const FSTraverseIterator &);
};


bool operator==(const FSTraverseIterator &,
                const FSTraverseIterator &);
bool operator!=(const FSTraverseIterator &,
                const FSTraverseIterator &);



// implementation of templates
template<typename OUT_ITER>
void FSGraph::getAdjacents(const QFileInfo & file,
                             OUT_ITER          outIter)
{
    if (file.isDir())
    {
        QDir
            dir(file.absoluteFilePath());

        QDir::Filters
            dirFilters = QDir::Dirs
                         | QDir::NoDotAndDotDot;
        if (m_filesAllowed)
        {
            dirFilters |= QDir::Files;
        }

        QFileInfoList
            entries = dir.entryInfoList(dirFilters);
        foreach (QFileInfo entry, entries)
        {
            *outIter = entry;
            ++outIter;
        }
    }
}

#endif // FSTRAVERSEITERATOR_H
